package logger2.filter;

import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import logger2.Level;
import logger2.LogRecord;
import logger2.environment.Environment;
import logger2.filter.events.ConfigurableFilterChangeEvent;

public class ConfigurableFilter extends Filter {
	private boolean filterOnLogger = false;
	private String regexLogger = "";
	private boolean acceptChilds=true;
	private Pattern patternLogger = Pattern.compile(".*\\.("+regexLogger+")\\..*");

	private boolean filterOnLevel=false;
	private Level level = Level.INFO;
	private boolean acceptHigher = true;

	private boolean filterOnMessage = false;
	private String regexMessage = ".*";
	private Pattern patternMessage = Pattern.compile(regexMessage);


	private boolean filterOnThread = false;
	private String regexThread = ".*";
	private Pattern patternThread = Pattern.compile(regexThread);

	private boolean filterOnClass = false;
	private String regexClass = ".*";
	private Pattern patternClass = Pattern.compile(regexClass);

	private boolean filterOnTime = false;
	private boolean hasLowLimit = false;
	private long lowLimit = 0;
	private boolean hasHighLimit = false;
	private long highLimit = 0;

	public ConfigurableFilter(Environment env) {
		super(env);
	}

	public ConfigurableFilter(Environment env, String name) {
		super(env, name);
	}

	public boolean isFilterOnLogger() {
		return filterOnLogger;
	}

	public String getRegexLogger() {
		return regexLogger;
	}

	public boolean isAcceptChilds() {
		return acceptChilds;
	}


	public void setLoggerFilter(boolean filterOnLogger, String regexLogger, boolean acceptChilds) throws PatternSyntaxException {
		ConfigurableFilterChangeEvent ev = new ConfigurableFilterChangeEvent(this);

		if(this.filterOnLogger != filterOnLogger)
			ev.filterOnLogger = true;
		this.filterOnLogger = filterOnLogger;

		if(!this.regexLogger.equals(regexLogger))
			ev.regexLogger = true;
		this.regexLogger = regexLogger;

		if(this.acceptChilds != acceptChilds)
			ev.acceptChilds = true;
		this.acceptChilds = acceptChilds;

		if(ev.containsChange()) {
			if(acceptChilds) {
				patternLogger = Pattern.compile(".*\\.("+regexLogger+")\\..*");
			} else {
				patternLogger = Pattern.compile(".*\\.("+regexLogger+")");
			}
			fireEvent(ev);
		}
	}

	public boolean isFilterOnLevel() {
		return filterOnLevel;
	}

	public Level getLevel() {
		return level;
	}

	public boolean isAcceptHigher() {
		return acceptHigher;
	}


	public void setLevelFilter(boolean filterOnLevel, Level level, boolean acceptHigher) {
		ConfigurableFilterChangeEvent ev = new ConfigurableFilterChangeEvent(this);

		if(this.filterOnLevel != filterOnLevel)
			ev.filterOnLevel = true;
		this.filterOnLevel = filterOnLevel;

		if(!this.level.equals(level))
			ev.level = true;
		this.level = level;

		if(this.acceptHigher != acceptHigher)
			ev.acceptHigher = true;
		this.acceptHigher = acceptHigher;

		if(ev.containsChange()) {
			fireEvent(ev);
		}
	}	

	public boolean isFilterOnMessage() {
		return filterOnMessage;
	}

	public String getRegexMessage() {
		return regexMessage;
	}

	public void setMessageFilter(boolean filterOnMessage, String regexMessage) {
		ConfigurableFilterChangeEvent ev = new ConfigurableFilterChangeEvent(this);

		if(this.filterOnMessage != filterOnMessage)
			ev.filterOnMessage = true;
		this.filterOnMessage = filterOnMessage;

		if(!this.regexMessage.equals(regexMessage))
			ev.regexMessage = true;
		this.regexMessage = regexMessage;

		if(ev.containsChange()) {
			patternMessage = Pattern.compile(regexMessage);
			fireEvent(ev);
		}
	}

	public boolean isFilterOnThread() {
		return filterOnThread;
	}

	public String getRegexThread() {
		return regexThread;
	}

	public void setThreadFilter(boolean filterOnThread, String regexThread) {
		ConfigurableFilterChangeEvent ev = new ConfigurableFilterChangeEvent(this);

		if(this.filterOnThread != filterOnThread)
			ev.filterOnThread = true;
		this.filterOnThread = filterOnThread;

		if(!this.regexThread.equals(regexThread))
			ev.regexThread = true;
		this.regexThread = regexThread;

		if(ev.containsChange()) {
			patternThread = Pattern.compile(regexThread);
			fireEvent(ev);
		}
	}

	public boolean isFilterOnClass() {
		return filterOnClass;
	}

	public String getRegexClass() {
		return regexClass;
	}

	public void setClassFilter(boolean filterOnClass, String regexClass) {
		ConfigurableFilterChangeEvent ev = new ConfigurableFilterChangeEvent(this);

		if(this.filterOnClass != filterOnClass)
			ev.filterOnClass = true;
		this.filterOnClass = filterOnClass;

		if(!this.regexClass.equals(regexClass))
			ev.regexClass = true;
		this.regexClass = regexClass;

		if(ev.containsChange()) {
			patternClass = Pattern.compile(regexClass);
			fireEvent(ev);
		}
	}

	public boolean isFilterOnTime() {
		return filterOnTime;
	}

	public boolean hasLowLimit() {
		return hasLowLimit;
	}

	public long getLowLimit() {
		return lowLimit;
	}

	public boolean hasHighLimit() {
		return hasHighLimit;
	}

	public long getHighLimit() {
		return highLimit;
	}

	public void setTimeFilter(boolean filterOnTime, boolean hasLowLimit, long lowLimit, boolean hasHighLimit, long highLimit) {
		ConfigurableFilterChangeEvent ev = new ConfigurableFilterChangeEvent(this);

		if(this.filterOnTime != filterOnTime)
			ev.filterOnTime = true;
		this.filterOnTime = filterOnTime;
		
		if(this.hasLowLimit != hasLowLimit)
			ev.hasLowLimit = true;
		this.hasLowLimit = hasLowLimit;
		
		if(this.lowLimit != lowLimit)
			ev.lowLimit = true;
		this.lowLimit = lowLimit;
		
		if(this.hasHighLimit != hasHighLimit)
			ev.hasHighLimit = true;
		this.hasHighLimit = hasHighLimit;
		
		if(this.highLimit != highLimit)
			ev.highLimit = true;
		this.highLimit = highLimit;

		if(ev.containsChange()) {
			fireEvent(ev);
		}
	}

	@Override
	public boolean accept(LogRecord record) {
		if(filterOnLogger &&
				!patternLogger.matcher(record.getLogger()).matches()) {
			return false;
		}

		if(filterOnLevel && (
				(acceptHigher && record.getLvl().compareTo(level)<0) ||
				(!acceptHigher && !record.getLvl().equals(level))
				)) {
			return false;
		}

		if(filterOnMessage &&
				!patternMessage.matcher(record.getMessage()).matches()) {
			return false;
		}

		if(filterOnThread &&
				!patternThread.matcher(record.getThreadName()).matches()) {
			return false;
		}

		if(filterOnClass &&
				!patternClass.matcher(record.getStackTraceElement().getClassName()).matches()) {
			return false;
		}

		if(filterOnTime && (
				(hasLowLimit && record.getTime()<lowLimit) ||
				(hasHighLimit && record.getTime()<highLimit)
				)) {
			return false;
		}

		return true;
	}

	@Override
	public <T> T acceptVisitor(FilterVisitor<T> visitor) {
		return visitor.visitConfigurableFilter();
	}

}
